/*
 * Copyright 2011 Angel Sanadinov
 *
 * This file is part of VBox WMI.
 *
 * VBox WMI is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * VBox WMI is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with VBox WMI.  If not, see <http://www.gnu.org/licenses/>.
 */

package ApplicationManagement;

import Utilities.Constants;
import Managers.DatabaseManager;
import Managers.LogsManager;
import Managers.SessionsManager;
import Managers.PermissionsManager;
import Managers.VBoxConnectionsManager;
import Utilities.ConfigurationReader;
import Utilities.ParameterNames;
import java.io.FileNotFoundException;
import java.io.IOException;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.UnavailableException;
import javax.servlet.http.HttpServlet;

/**
 * Controls the startup and shutdown of the application. <br><br>
 *
 * <b>Note:</b> <i>This servlet should be the first one to execute its init() method,
 * ie: it should load on startup and have the lowest startup order!</i>
 *
 * @author Angel Sanadinov
 */
public class ApplicationControl extends HttpServlet
{
    private Thread logsManagerThread;                    //logs manager thread
    private DatabaseManager databaseManager;             //database manager
    private LogsManager logsManager;                     //logs manager
    private SessionsManager sessionsManager;             //sessions manager
    private PermissionsManager permissionsManager;       //permissions manager
    private VBoxConnectionsManager connectionsManager;   //virtualbox connections manager

    /**
     * Initializes the application; starts and configures all managers.
     *
     * @throws ServletException
     */
    @Override
    public void init() throws ServletException
    {
        ServletContext servletContext = getServletContext();
        ServletConfig servletConfiguration = getServletConfig();

        try
        {
            ConfigurationReader applicationConfiguration =
                    ConfigurationReader.getReader(
                        servletContext.getRealPath(
                            servletConfiguration.getInitParameter(ParameterNames.CONFIGURATION_FILE)));

            if(applicationConfiguration == null)
                throw new UnavailableException("The supplied configuration file is invalid.");
            else
                ;

            //setting innitialization parameters for filters and services
            servletContext.setInitParameter(ParameterNames.COMPRESSION_ALGORITHM,
                    applicationConfiguration.getParameter(ConfigurationReader.PARAM_COMPRESSION_ALGORITHM));
            servletContext.setInitParameter(ParameterNames.PASSWORD_HASHING_ALGORITHM,
                    applicationConfiguration.getParameter(ConfigurationReader.PARAM_PASSWORD_HASHING_ALGORITHM));
            servletContext.setInitParameter(ParameterNames.DEFAULT_CHARSET,
                    applicationConfiguration.getParameter(ConfigurationReader.PARAM_DEFAULT_CHARSET));
            servletContext.setInitParameter(ParameterNames.MAX_OPERATION_WAIT_TIME,
                    applicationConfiguration.getParameter(ConfigurationReader.PARAM_MAX_OPERATION_WAIT_TIME));
            servletContext.setInitParameter(ParameterNames.REPORTING_LOGGING_LEVEL,
                    applicationConfiguration.getParameter(ConfigurationReader.PARAM_REPORTING_LOGGING_LEVEL));
            servletContext.setInitParameter(ParameterNames.REMOTE_SESSION_TYPE,
                    applicationConfiguration.getParameter(ConfigurationReader.PARAM_REMOTE_VBOX_SESSION_TYPE));
            //done

            logsManager = new LogsManager(applicationConfiguration.getLogsManagerConfiguration(), servletContext);
            
            if(logsManager == null || !logsManager.isValid())
            {
                destroy();
                throw new UnavailableException("Failed to initialize logging manager");
            }
            else
                ;

            logsManagerThread = new Thread(logsManager);
            logsManagerThread.setContextClassLoader(null);
            logsManagerThread.start();
            servletContext.setAttribute(Constants.CONTEXT_LOGS_MANAGER, logsManager);

            

            databaseManager = new DatabaseManager(logsManager, applicationConfiguration.getDatabaseManagerConfiguration());

            if(databaseManager == null || !databaseManager.isValid())
            {
                destroy();
                throw new UnavailableException("Failed to initialize database manager");
            }
            else
                ;

            servletContext.setAttribute(Constants.CONTEXT_DATABASE_MANAGER, databaseManager);
            logsManager.setDatabaseManagerReference(databaseManager);

            connectionsManager = new VBoxConnectionsManager(databaseManager, logsManager, applicationConfiguration.getConnectionsManagerConfiguration());

            if(connectionsManager == null || !connectionsManager.isValid())
            {
                destroy();
                throw new UnavailableException("Failed to initialize VirtualBox connections manager");
            }
            else
                ;

            servletContext.setAttribute(Constants.CONTEXT_VBOX_CONNECTIONS_MANAGER, connectionsManager);

            sessionsManager = new SessionsManager(databaseManager, logsManager, connectionsManager,
                                                  applicationConfiguration.getSessionsManagerConfiguration());

            if(sessionsManager == null || !sessionsManager.isValid())
            {
                destroy();
                throw new UnavailableException("Failed to initialize sessions manager");
            }
            else
                ;

            servletContext.setAttribute(Constants.CONTEXT_SESSIONS_MANAGER, sessionsManager);

            permissionsManager = new PermissionsManager(databaseManager, logsManager, sessionsManager, connectionsManager,
                                                        applicationConfiguration.getPermissionsManagerConfiguration());

            if(permissionsManager == null || !permissionsManager.isValid())
            {
                destroy();
                throw new UnavailableException("Failed to initialize permissions manager");
            }
            else
                ;

            servletContext.setAttribute(Constants.CONTEXT_PERMISSIONS_MANAGER, permissionsManager);

            
        }
        catch(FileNotFoundException e)
        {
            throw new UnavailableException("The configuration file is missing or is not accessible");
        }
        catch(IOException e)
        {
            throw new UnavailableException("An I/O error occurred during the reading of the configuration file: "
                                           + e.getMessage());
        }
    }

    /**
     * Terminates the application; stops the managers and releases all resources.
     */
    @Override
    public void destroy()
    {//managers are stopped in the reverse initialization order
        ServletContext context = getServletContext();
        context.removeAttribute(Constants.CONTEXT_LOGS_MANAGER);
        context.removeAttribute(Constants.CONTEXT_DATABASE_MANAGER);
        context.removeAttribute(Constants.CONTEXT_SESSIONS_MANAGER);
        context.removeAttribute(Constants.CONTEXT_PERMISSIONS_MANAGER);
        context.removeAttribute(Constants.CONTEXT_VBOX_CONNECTIONS_MANAGER);

        if(permissionsManager != null)
            permissionsManager.stop();
        else
            ;

        if(sessionsManager != null)
            sessionsManager.stop();
        else
            ;

        if(connectionsManager != null)
            connectionsManager.stop();
        else
            ;
        
        if(databaseManager != null)
            databaseManager.stop();
        else
            ;

        if(logsManager != null)
            logsManager.stop();
        else
            ;

        permissionsManager = null;
        sessionsManager = null;
        connectionsManager = null;
        databaseManager = null;
        logsManager = null;
    }
}
